
#weather.py
#用于获取天气数据
#实现API调用，将json转化为python内建类型

import json
import requests
import RPi.GPIO as GPIO
from setting import *

#天气获取部分，使用心知天气的API

KEY = '4r9bergjetiv1tsd'  # API key
UID = "U785B76FC9"  # 用户ID
NOWAPI = 'https://api.seniverse.com/v3/weather/now.json'  
FORECASTAPI = 'https://api.seniverse.com/v3/weather/daily.json'
UNIT = 'c'  # 单位
LANGUAGE = 'zh-Hans' # 查询结果的返回语言

#返回城市列表，计划用于设置位置，暂时没用上。
def getCityList():
	pass

def getWeatherNow(location=LOCATION):
	result = requests.get(NOWAPI,params={
		'key':KEY,
		'location':location,
		'language':LANGUAGE,
		'unit':UNIT
	},timeout=1)
	result = json.loads(result.text)
	return result

def getWeatherForecast(location=LOCATION):
	result = requests.get(FORECASTAPI,params={
		'key':KEY,
		'location':location,
		'language':LANGUAGE,
		'unit':UNIT
	},timeout=1)
	result = json.loads(result.text)
	return result


#获取空气质量部分，爬取PM25
#当然如果能够直接使用API就好了，可惜没钱
from bs4 import BeautifulSoup
import traceback
import re


def _getHTMLText(url):
	try:
		r = requests.get(url, timeout=30)
		r.raise_for_status()
		r.encoding = r.apparent_encoding
		return r.text
	except:
		print('网页获取失败')
		traceback.print_exc()

def _updatePM25Data(url):
	#从指定URL更新数据
	#数据格式为字典型，有cityName，AQI，level，PM2_5，pos,time六个键
	#分别代表城市名，AQI指数，污染等级，PM2.5浓度，监测站数据（指向一个字典列表）和提供数据的时间
	soup = BeautifulSoup(_getHTMLText(url),'html.parser')
	c_main = soup.find(attrs={"class":"c_main"})
	cityName = c_main.find(attrs={"class":"cm_location"}).string
	time = re.search(r'(\d.*)',c_main.find(attrs={"class":"cm_updatetime"}).text).group(1)
	aqi = c_main.find(attrs={"class":"cm_area_big"}).string
	level = c_main.find(attrs={"class":"cm_area_level"}).span.string
	pm_25 = re.search(r'(\d+) 微克',c_main.find(attrs={"class":"cm_nongdu"}).string).group(1)
	poslist = []
	for pos in soup.find_all(attrs={"class":"ci_jiance_line"}):
		posdict = {
			"posName":pos.span.string,
			"posAQI":pos.div.span.strong.string,
			"posLevel":pos.div.span.b.string,
			"posPM2_5":pos.find(attrs={"class":"ci_pm25num"}).string
		}
		poslist.append(posdict)
	return {"cityName":cityName,
			"AQI":aqi,
			"level":level,
			"PM_25":pm_25,
			"pos":poslist,
			"time":time}

def getPM25(location=LOCATION):
	result = _updatePM25Data('http://m.pm25.com/wap/city/'+location+'.html')
	return result


#室内气象获取部分	
def getWeatherHouse():
	times = 0
	while True:
		GPIO.setmode(GPIO.BCM)
		time.sleep(1)
		channel = 18
		data = []
		j = 0

		GPIO.setup(channel, GPIO.OUT)
		GPIO.output(channel, GPIO.LOW)
		time.sleep(0.02)
		GPIO.output(channel, GPIO.HIGH)
		GPIO.setup(channel, GPIO.IN)

		while GPIO.input(channel) == GPIO.LOW:
			continue
		while GPIO.input(channel) == GPIO.HIGH:
			continue

		while j < 40:
			k = 0
			while GPIO.input(channel) == GPIO.LOW:
				continue
			while GPIO.input(channel) == GPIO.HIGH:
				k += 1
				if k > 100:
					break
			if k < 8:
				data.append(0)
			else:
				data.append(1)
			j += 1
		
		GPIO.cleanup()
		
		humidity_bit = data[0:8]
		humidity_point_bit = data[8:16]
		temperature_bit = data[16:24]
		temperature_point_bit = data[24:32]
		check_bit = data[32:40]

		humidity = 0
		humidity_point = 0
		temperature = 0
		temperature_point = 0
		check = 0

		for i in range(8):
			humidity += humidity_bit[i] * 2 ** (7-i)
			humidity_point += humidity_point_bit[i] * 2 ** (7-i)
			temperature += temperature_bit[i] * 2 ** (7-i)
			temperature_point += temperature_point_bit[i] * 2 ** (7-i)
			check += check_bit[i] * 2 ** (7-i)

		tmp = humidity + humidity_point + temperature + temperature_point
		
		if check == tmp:
			houseWeather =  {"temperature":temperature,"humidity":humidity}
			print (houseWeather)
			return houseWeather
			# print "wrong"
			# print "temperature :", temperature, "*C, humidity :", humidity, "% check :", check, ", tmp :", tmp
		times = times + 1
		if times >= MAX_REQUEST_TIME:
			return {"temperature":"Unknown","temperature":"Unknown"}


import time
import threading
#封装类
#TODO 封装天气与空气数据，以及室内气象数据，包含强制更新方法，获取数据方法
class Weather:
	__data = {}
	__lastUpdateTime = {
		'weather':0,
		'pm25':0,
		'houseWeather':0
	}
	#更新数据的函数字典，舍去一部分无用数据
	__updateMethod = {
		'weather': (lambda : getWeatherNow()['results'][0]['now']),
		'pm25': getPM25,
		'houseWeather': getWeatherHouse
	}
	#处理数据的函数字典，按可读字符串输出
	__outputMethod = {
		'weather' : ( lambda data : "现在天气为{}，室外温度为{}摄氏度，湿度{},{}风风力{}级。".format(
			data['text'],
			data['temperature'],
			data['humidity'],
			data['wind_direction'],
			data['wind_scale'])
		),
		'pm25' : ( lambda data : "{}现在室外空气质量评级为{},AQI指数为{}，PM2.5浓度为{}毫克每立方米。".format(
			data['cityName'],
			data['level'],
			data['AQI'],
			data['PM_25'])
		),
		'houseWeather' : (lambda data : "现在室内温度{}摄氏度，湿度{}。".format(
			data['temperature'],
			data['humidity']
			)
		)
	}

	def forceUpdate(self):
		self.update('weather')
		self.update('pm25')	
		self.update('houseWeather')

	def update(self,task):
		if(task in self.__updateMethod.keys()):
			self.__data[task] = self.__updateMethod[task]()
		else:
			print('未知的天气更新任务')
			traceback.print_exc()
			return
		if(task in self.__lastUpdateTime.keys()):
			self.__lastUpdateTime[task] = time.time()
		else:
			print("无法保存更新时间")
			traceback.print_exc()
			return
		if(AUTO_UPDATE_WEATHER[task]):
			t = threading.Timer(WEATHER_UPDATE_INTERVAL[task],lambda : self.update(task))
			t.start()

	
	def getWeather(self,task):
		if(task in self.__data.keys()):
			if(time.time() - self.__lastUpdateTime[task] > WEATHER_UPDATE_INTERVAL[task]):
				self.update(task)
			return self.__data[task]
		else:
			print('未知的天气任务')
			traceback.print_exc()

	def pretty(self,task):
		if(task in self.__data.keys()):
			return self.__outputMethod[task](self.__data[task])
		else:
			print('未知的天气任务')
			traceback.print_exc()
		
	def __init__(self):
		self.forceUpdate()
		
